#include <intrins.h>
#define LCD_DATA P0

sbit LCD_RS = P2 ^ 2;
sbit LCD_RW = P2 ^ 1;

sbit LCD_CS1 = P2 ^ 4;
sbit LCD_CS2 = P2 ^ 3;

sbit LCD_E = P2 ^ 0;

void lcd_delay_ms(unsigned int ms)
{
    unsigned int i, j;
    for (i = 0; i < ms; i++)
    {
        for (j = 0; j < 123; j++)
            ;
    }
}
/**
 * us级别的延时函数
 */
void lcd_delay_us(unsigned int t)
{
    while (t--)
    {
        // no operation，1个NOP消耗1个机器周期
        // 晶振12MHZ  1/12000000  = 1/12 us  时钟周期
        // 1个机器周期等于12个时钟周期 1us
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
        _nop_();
    }
}

/**
 * 读取忙状态标识
 * @param controller 0 cs1，1 cs2
 * @return 0: 准备好 ，1：繁忙，没有准备好
 */
bit lcd_read_busy(unsigned char controller)
{
    bit bf_status;
    LCD_RS = 0; // 命令
    LCD_RW = 1; // 读取
    LCD_CS1 = (controller == 1);
    LCD_CS2 = (controller == 0);
    // 读取之前重置线路的状态
    LCD_DATA = 0xFF;
    LCD_E = 1;
    // 等待数据准备
    lcd_delay_us(2);
    // 从线路接收数据
    // 取DB7  BF位，1表示繁忙
    bf_status = (LCD_DATA & 0x80) ? 1 : 0;
    LCD_E = 0;
    return bf_status;
}

/**
 * 发送指令
 * @param cmd 一个字节的指令数据
 * @param controller 0 使用CS1， 1 使用CS2
 */
void lcd_write_command(unsigned char cmd, unsigned char controller)
{
    while (lcd_read_busy(controller))
    {
        // 等待lcd准备好
    }

    LCD_DATA = cmd;
    // 发送 = 写
    LCD_RW = 0;
    // 指令 = 命令
    LCD_RS = 0;
    // 发送给CS1 LCD_CS1 = 0
    LCD_CS1 = (controller == 1);
    LCD_CS2 = (controller == 0);

    LCD_E = 1;
    // 拉高保持
    lcd_delay_us(2);
    LCD_E = 0;
}

/**
 * 发送数据
 * @param dat 一个字节的数据
 * @param controller 0 使用CS1， 1 使用CS2
 */
void lcd_write_data(unsigned char dat, unsigned char controller)
{
    LCD_DATA = dat;
    // 发送 = 写
    LCD_RW = 0;
    // 数据
    LCD_RS = 1;
    // 发送给CS1 LCD_CS1 = 0
    LCD_CS1 = (controller == 1);
    LCD_CS2 = (controller == 0);

    LCD_E = 1;
    // 拉高保持
    lcd_delay_us(2);
    LCD_E = 0;
}

void lcd_init()
{
    lcd_write_command(0x3F, 0);
    lcd_write_command(0x3F, 1);
}

/**
 * 在指定的页，指定的列上显示一个byte
 * @param col 0~127 ， 0~63 cs1  64~127 cs2
 */
void lcd_display_one_byte_in_page(
    unsigned char page,
    unsigned char col,
    unsigned char one_byte)
{
    // 根据col 数据计算cs1和cs2的片选
    bit control = col >= 64;
    lcd_write_command(0xB8 | page, control);
    // printf("page:%d\n", (int)page);
    // 0x40 | (col % 64)    0x40 | col
    lcd_write_command(0x40 | col, control);
    // printf("col:%d\n", (int)((col % 64)));
    lcd_write_data(one_byte, control);
}

/**
 * 指定page和col，指定数据，指定写入多少次，连续性写入
 * @param col 0~127 写入的起始位置
 */
void lcd_display_same_bytes_in_page(
    unsigned char page,
    unsigned char col,
    unsigned char one_byte,
    unsigned int count)
{
    unsigned char i;
    if (col >= 64)
    {
        // 写cs2，不会跨屏幕
        lcd_write_command(0xB8 | page, 1);
        lcd_write_command(0x40 | col, 1);
        for (i = 0; i < count; i++)
        {
            lcd_write_data(one_byte, 1);
        }
    }
    else if (col + count < 64)
    {
        // 写cs1，不会跨屏幕
        lcd_write_command(0xB8 | page, 0);
        lcd_write_command(0x40 | col, 0);
        for (i = 0; i < count; i++)
        {
            lcd_write_data(one_byte, 0);
        }
    }
    else
    {
        // 先写cs1，再写cs2
        lcd_write_command(0xB8 | page, 0);
        lcd_write_command(0x40 | col, 0);
        for (i = 0; i < 64 - col; i++)
        {
            lcd_write_data(one_byte, 0);
        }

        lcd_write_command(0xB8 | page, 1);
        lcd_write_command(0x40 | col, 1);
        for (i = 0; i < count - (64 - col); i++)
        {
            lcd_write_data(one_byte, 1);
        }
    }
}

/**
 * 传入一个数组，在指定位置依次写入数据
 */
void lcd_display_bytes_in_page(
    unsigned char page,
    unsigned char col,
    unsigned char bytes[],
    unsigned char length)
{
    unsigned char i;
    if (col >= 64)
    {
        // 写右侧 CS2
        lcd_write_command(0xB8 | page, 1);
        lcd_write_command(0x40 | col, 1);
        for (i = 0; i < length; i++)
        {
            lcd_write_data(bytes[i], 1);
        }
    }
    else if (col + length < 64)
    {
        // 写左侧 CS1
        lcd_write_command(0xB8 | page, 0);
        lcd_write_command(0x40 | col, 0);
        for (i = 0; i < length; i++)
        {
            lcd_write_data(bytes[i], 0);
        }
    }
    else
    {
        // 写左侧
        lcd_write_command(0xB8 | page, 0);
        lcd_write_command(0x40 | col, 0);
        for (i = 0; i < 64 - col; i++)
        {
            lcd_write_data(bytes[i], 0);
        }

        // 写右侧
        lcd_write_command(0xB8 | page, 1);
        lcd_write_command(0x40, 1);
        for (i = 0; i < length - (64 - col); i++)
        {
            lcd_write_data(bytes[i + 64 - col], 1);
        }
    }
}

void lcd_clean_screen()
{
    unsigned char page;
    unsigned char i;
    for (page = 0; page < 8; page++)
    {
        lcd_write_command(0xB8 | page, 0);
        lcd_write_command(0x40 | 0, 0);
        for (i = 0; i < 64; i++)
        {
            lcd_write_data(0x00, 0);
        }

        lcd_write_command(0xB8 | page, 1);
        lcd_write_command(0x40 | 0, 1);
        for (i = 0; i < 64; i++)
        {
            lcd_write_data(0x00, 1);
        }
    }
}
